
/*
 * Hlpay-Plus aggregate payment system. Copyright
 * (c) 2022-2023 Hlpay Team Copyright has the right of final interpretation.
 */

package com.hlkj.pay.app.token.impl;

import java.time.LocalDateTime;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.date.LocalDateTimeUtils;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.token.ITokenAuthService;
import com.hlkj.pay.app.token.ext.AuthTokenExtPt;
import com.hlkj.pay.common.CommonResultCode;
import com.hlkj.pay.common.TenantSecretContext;
import com.hlkj.pay.common.constants.TenantAuthConstant;
import com.hlkj.pay.common.constants.TokenConstant;
import com.hlkj.pay.enums.AuthTokenEnum;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.infrastructure.model.token.TokenSecretDO;
import com.hlkj.pay.service.token.ITokenService;
import com.hlkj.pay.service.token.impl.MessageSendService;
import com.hlkj.pay.service.token.impl.TokenCommonService;
import com.hlkj.pay.util.TokenUtils;
import com.hlkj.pay.vo.common.req.token.*;
import com.hlkj.pay.vo.common.resp.token.MfaResp;
import com.hlkj.pay.vo.common.resp.token.OauthTokenResp;
import com.hlkj.pay.vo.common.resp.token.TokenInfoResp;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @date 2024/09/27 17:51
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class TenantAuthServiceImpl implements ITokenAuthService {

    private final ExtensionExecutor extensionExecutor;

    private final ITokenService tokenService;
    private final TokenCommonService tokenCommonService;

    private final MessageSendService messageSendService;

    @Value("${auth.authCodeRequestTime:60}")
    private Integer authCodeRequestTime;

    @Override
    public CommonResult<OauthTokenResp> webUserToken(PasswordTokenRequest passwordTokenRequest) {
        TokenSecretDO tenantSecretDO = tokenService.queryTenantInfoByDomain();
        if (tenantSecretDO == null) {
            return CommonResult.error(CommonResultCode.APP_NOT_EXIST);
        }
        TenantSecretContext.set(tenantSecretDO);
        return extensionExecutor.execute(AuthTokenExtPt.class, BizScenario.valueOf(TenantAuthConstant.ADMIN_PASSWORD), ex -> ex.oauthToken(passwordTokenRequest));
    }
    @Override
    public CommonResult<OauthTokenResp> webUserToken(PhoneTokenRequest phoneTokenRequest) {
        TokenSecretDO tenantSecretDO = tokenService.queryTenantInfoByDomain();
        if (tenantSecretDO == null) {
            return CommonResult.error(CommonResultCode.APP_NOT_EXIST);
        }
        TenantSecretContext.set(tenantSecretDO);
        return extensionExecutor.execute(AuthTokenExtPt.class, BizScenario.valueOf(TenantAuthConstant.ADMIN_PHONE_AUTH_CODE), ex -> ex.oauthToken(phoneTokenRequest));
    }
    
    @Override
    public CommonResult<MfaResp> mfaToken(AuthMfaTokenRequest authMfaTokenRequest) {
        TokenInfoResp tokenInfoResp = TokenUtils.parseToken(authMfaTokenRequest.getToken());
        AuthTokenEnum.AUTH_TOKEN_TYPE authTokenType = AuthTokenEnum.AUTH_TOKEN_TYPE.from(tokenInfoResp.getAuthTokenType());
        switch (authTokenType) {
            case MFA_ACCESS_TOKEN:
                break;
            default:
                return CommonResult.error(CommonResultCode.TOKEN_PARAMETER_ERROR);
        }
        TokenSecretDO tenantSecretDO = tokenService.queryTenantInfoByAppId(tokenInfoResp.getAppId());
        if (tenantSecretDO == null) {
            return CommonResult.error(CommonResultCode.APP_NOT_EXIST);
        }
        // Long authCodeTime = messageSendService.authCodeTime(tokenInfoResp.getUserId(), tenantSecretDO);
        Long authCodeTime = 0L;
        if (authCodeTime != null) {
            long timestamp = LocalDateTimeUtils.datatimeToTimestamp(LocalDateTime.now());
            if ((timestamp - authCodeTime) < authCodeRequestTime * TokenConstant.THOUSAND) {
                CommonResult<MfaResp> commonResult = CommonResult.error(CommonResultCode.TOKEN_AUTH_CODE_NUM_REQUEST);
                MfaResp mfaResp = new MfaResp();
                mfaResp.setAuthCodeTime(authCodeTime);
                commonResult.setData(mfaResp);
                return commonResult;
            }
        }
        return messageSendService.authCode(tokenInfoResp.getUserId(), authMfaTokenRequest.getType(), tenantSecretDO);
    }

    @Override
    public CommonResult<OauthTokenResp> mfaCheckToken(AuthMfaCheckTokenRequest authMfaCheckTokenRequest) {
        TokenInfoResp tokenInfoResp = TokenUtils.parseToken(authMfaCheckTokenRequest.getToken());
        AuthTokenEnum.AUTH_TOKEN_TYPE authTokenType = AuthTokenEnum.AUTH_TOKEN_TYPE.from(tokenInfoResp.getAuthTokenType());
        switch (authTokenType) {
            case MFA_ACCESS_TOKEN:
                break;
            default:
                return CommonResult.error(CommonResultCode.TOKEN_PARAMETER_ERROR);
        }
        TokenSecretDO tenantSecretDO = tokenService.queryTenantInfoByAppId(tokenInfoResp.getAppId());
        if (tenantSecretDO == null) {
            return CommonResult.error(CommonResultCode.APP_NOT_EXIST);
        }
        boolean checkAuthCode = messageSendService.checkAuthCode(tokenInfoResp.getUserId(), authMfaCheckTokenRequest.getAuthCode(), tenantSecretDO);
        if (!checkAuthCode) {
            return CommonResult.error(CommonResultCode.AUTH_CODE_ERROR);
        }
        OauthTokenResp oauthTokenResp = tokenCommonService.userToken(tenantSecretDO, tokenInfoResp.getUserId(),tokenInfoResp.getSn());
        oauthTokenResp.setMfaLoginType(CommonEnum.YES_NO_TYPE.NO.getCode());
        tokenCommonService.addTenantAuthLog(tenantSecretDO, oauthTokenResp, AuthTokenEnum.AUTH_TOKEN_REQUEST_TYPE.MFA_TOKEN);
        return CommonResult.success(oauthTokenResp);
    }

    @Override
    public CommonResult<OauthTokenResp> clientToken(AuthTokenClientRequest authTokenClientRequest) {
        TokenSecretDO tenantSecretDO = tokenService.queryTenantInfoByAppId(authTokenClientRequest.getAppId());
        if (tenantSecretDO == null) {
            return CommonResult.error(CommonResultCode.APP_NOT_EXIST);
        }
        // if (!authTokenClientRequest.getAppSecret().equals(tenantSecretDO.getSecret())) {
        //     return CommonResult.error(TenantResultCode.TOKEN_CLIENT_SECRET_ERROR);
        // }
        TenantSecretContext.set(tenantSecretDO);
        return extensionExecutor.execute(AuthTokenExtPt.class, BizScenario.valueOf(TenantAuthConstant.CLIENT), ex -> ex.oauthToken(authTokenClientRequest));
    }
    @Override
    public CommonResult<OauthTokenResp> clientToken(PhoneTokenRequest phoneTokenRequest) {
        TokenSecretDO tenantSecretDO = tokenService.queryTenantInfoByDomain();
        if (tenantSecretDO == null) {
            return CommonResult.error(CommonResultCode.APP_NOT_EXIST);
        }
        TenantSecretContext.set(tenantSecretDO);
        return extensionExecutor.execute(AuthTokenExtPt.class, BizScenario.valueOf(TenantAuthConstant.CLIENT_PHONE_AUTH_CODE), ex -> ex.oauthToken(phoneTokenRequest));
    }
    
    @Override
    public CommonResult<OauthTokenResp> refreshToken(String token) {
        boolean checkToken = tokenCommonService.checkToken(token);
        if (!checkToken) {
            return CommonResult.error(CommonResultCode.TOKEN_PARAMETER_ERROR);
        }
        TokenInfoResp refreshTokenInfoResp = TokenUtils.parseToken(token);
        if (refreshTokenInfoResp.getExpireTime() < System.currentTimeMillis() / TokenConstant.THOUSAND) {
            return CommonResult.error(CommonResultCode.TOKEN_PARAMETER_EXPIRE);
        }
        AuthTokenEnum.AUTH_TOKEN_TYPE authTokenType = AuthTokenEnum.AUTH_TOKEN_TYPE.from(refreshTokenInfoResp.getAuthTokenType());
        switch (authTokenType) {
            case ACCESS_TOKEN:
                return CommonResult.error(CommonResultCode.TOKEN_PARAMETER_ERROR);
            default:
        }
        TokenSecretDO tenantSecretDO = tokenService.queryTenantInfoByAppId(refreshTokenInfoResp.getAppId());
        if (tenantSecretDO == null) {
            return CommonResult.error(CommonResultCode.APP_NOT_EXIST);
        }
        OauthTokenResp oauthTokenResp = tokenCommonService.refreshToken(refreshTokenInfoResp, token, tenantSecretDO);
        tokenCommonService.addTenantAuthLog(tenantSecretDO, oauthTokenResp, AuthTokenEnum.AUTH_TOKEN_REQUEST_TYPE.REFRESH);
        return CommonResult.success(oauthTokenResp);
    }

    @Override
    public CommonResult<Boolean> checkToken(String token) {
        return CommonResult.success(tokenCommonService.checkToken(token));
    }

    @Override
    public CommonResult expireToken(String token) {
        TokenInfoResp tokenInfoResp = TokenUtils.parseToken(token);
        tokenCommonService.expireToken(tokenInfoResp);
        return CommonResult.success();
    }
}
